package com.thientan.controller

import grails.converters.JSON

import java.text.SimpleDateFormat

import org.hibernate.FetchMode
import org.hibernate.StaleObjectStateException
import org.hibernate.exception.ConstraintViolationException
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.dao.OptimisticLockingFailureException
import org.springframework.web.multipart.MultipartFile

import com.thientan.model.ChiTietThuChi
import com.thientan.model.HopDong
import com.thientan.model.NhanVien
import com.thientan.model.enums.LoaiChiTietThuChi
import com.thientan.model.enums.TrangThaiHopDong
import com.thientan.service.PhieuThuService
import com.thientan.utils.Constants

class PhieuThuController extends AbstractBaseController {
	PhieuThuService phieuThuService

	@Override
	public Class getDomainClass() {
		ChiTietThuChi.class
	}

	@Override
	public String getDomainName() {
		message(code : "default.thientan.messages.phieuThu")
	}

	@Override
	public Object getDomainService() {
		phieuThuService
	}

	@Override
	public String getDisplayFieldName() {
		null
	}

	@Override
	protected String getCustomSavingMsg(HopDong hd) {
		message(code : "default.thientan.messages.saveChiTietSuccess", args : [hd.maHd])
	}

	def savePhieuThu = {
		Double soTien = Double.valueOf(params["soTien"]) * Constants.RATE
		Date ngayLap = new SimpleDateFormat(Constants.DEFAULT_DATE_FORMAT).parse(params["ngayLap"])
		HopDong hd = HopDong.get(params["id"])
		hd.updateTraGop(soTien, ngayLap)
		Map result = updatePhieuThuOnHopDong(hd)
		render (result as JSON)
	}

	def suaHdTraHang = {
		HopDong hd = HopDong.get(params["id"])
		Date ngayLap = new SimpleDateFormat(Constants.DEFAULT_DATE_FORMAT).parse(params["ngayLap"])

		Map khauHaoParams = [
					"loaiChiTiet" : LoaiChiTietThuChi.KHAU_HAO,
					"ngayLap" : ngayLap,
					"soTien" : Double.valueOf(params["khauHao"]) * Constants.RATE,
					"nhanVien.id" : hd.nvThuNgan.id]
		ChiTietThuChi chiTietKhauHao = new ChiTietThuChi(khauHaoParams)
		Map traKhachParams = [
					"loaiChiTiet" : LoaiChiTietThuChi.TRA_KHACH,
					"ngayLap" : ngayLap,
					"soTien" : Double.valueOf(params["soTien"]) * Constants.RATE,
					"nhanVien.id" : hd.nvThuNgan.id]
		ChiTietThuChi chiTietTraKhach = new ChiTietThuChi(traKhachParams)

		hd.updateDenTraHang(TrangThaiHopDong.TRA_HANG)
		hd.addToChiTietThuChis(chiTietKhauHao)
		hd.addToChiTietThuChis(chiTietTraKhach)

		Map result = updatePhieuThuOnHopDong(hd)
		render (result as JSON)
	}

	def suaHdDenHang = {
		HopDong hd = HopDong.get(params["id"])

		if(params["tienCtyChiu"]) {
			updateHopDongToKhoThu(hd)
		} else {
			updateHopDongToDenHang(hd)
		}

		Map result = updatePhieuThuOnHopDong(hd)
		render (result as JSON)
	}

	private Map updatePhieuThuOnHopDong(HopDong hd) {
		Map result = [:]
		try {
			String msg
			HopDong savedHd = domainService.saveOrUpdate(hd)
			if(!hd.errors.allErrors){
				msg = message(code : "default.thientan.messages.updateDenTraHangSuccess",
						args : [
							hd.maHd,
							hd.trangThai.displayText.toUpperCase()
						])
				result = getFormMessage(true, "info", msg, null)
			} else {
				log.warn('Binding/Validation errors happended during saving: ' + hd.errors.allErrors)
				msg = message(code : "default.thientan.messages.updateDenTraHangFailed",
						args : [
							hd.maHd,
							hd.trangThai.displayText.toUpperCase()
						])
				result = getFormMessage(false, "error", msg, null)
			}
		} catch(OptimisticLockingFailureException ex) {
			log.error("OptimisticLockingFailureException happened during saving", ex)
			result = produceConcurrentUpdateMessage(displayFieldName)
		} catch(StaleObjectStateException ex) {
			log.error("StaleObjectStateException happened during saving", ex)
			result = produceConcurrentUpdateMessage(displayFieldName)
		} catch(DataIntegrityViolationException ex) {
			log.error("DataIntegrityViolationException happened during saving", ex)
			result = produceConstraintViolationMessage()
		} catch(ConstraintViolationException ex) {
			log.error("ConstraintViolationException happened during saving", ex)
			result = produceConstraintViolationMessage()
		} catch(Exception ex){
			log.error("Unexpected error happened during saving", ex)
			result = handleSaveExceptions(ex, result)
		}
		return result
	}

	private void updateHopDongToKhoThu(HopDong hd) {
		ChiTietThuChi ctyChiu = new ChiTietThuChi(
				"ngayLap" : new Date(),
				"loaiChiTiet" : LoaiChiTietThuChi.DEN_CTY,
				"soTien" : params["tienCtyChiu"],
				"ghiChu" : params["ghiChu"],
				"quyetToan" : true)
		hd.addToChiTietThuChis(ctyChiu)
		hd.updateDenTraHang(TrangThaiHopDong.KHO_THU)
	}

	private updateHopDongToDenHang(HopDong hd) {
		Date ngayLap = new Date()
		if(params["doiTruong"]) {
			ChiTietThuChi denDt = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"nhanVien" : NhanVien.findByMaNv(params["doiTruong"]),
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_DT,
					"soTien" : params["denDt"])
			ChiTietThuChi denNv1 = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"nhanVien" : NhanVien.findByMaNv(params["nvBan1"]),
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_NV,
					"soTien" : params["denNv1"])
			ChiTietThuChi denNv2 = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"nhanVien" : NhanVien.findByMaNv(params["nvBan2"]),
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_NV,
					"soTien" : params["denNv2"])

			ChiTietThuChi denCty = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_CTY,
					"soTien" : params["denCty"],
					"quyetToan" : true
					)
			hd.addToChiTietThuChis(denDt)
			hd.addToChiTietThuChis(denNv1)
			hd.addToChiTietThuChis(denNv2)
			hd.addToChiTietThuChis(denCty)
		} else {
			ChiTietThuChi denNv = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"nhanVien" : NhanVien.findByMaNv(params["nvBan1"]),
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_NV,
					"soTien" : params["denNv1"])

			ChiTietThuChi denCty = new ChiTietThuChi(
					"ngayLap" : ngayLap,
					"loaiChiTiet" : LoaiChiTietThuChi.DEN_CTY,
					"soTien" : params["denCty"],
					"quyetToan" : true)
			hd.addToChiTietThuChis(denNv)
			hd.addToChiTietThuChis(denCty)
		}
		hd.updateDenTraHang(TrangThaiHopDong.DEN_HANG)
	}

	def getCompensationRates = {
		String resourcePath = servletContext.getRealPath("") + File.separator + 'conf/rate.conf'
		Map result = [:]
		FileInputStream fis = null
		BufferedReader br = null

		try {
			fis = new FileInputStream(resourcePath)
			br = new BufferedReader(new InputStreamReader(new DataInputStream(fis)))
			Map rateMap = [:]

			String strLine
			//Read File Line By Line
			while ((strLine = br.readLine()) != null)   {
				if(strLine.contains(Constants.CONF_DEN_DT)) {
					rateMap["doiTruong"] = Integer.parseInt(strLine.substring(strLine.lastIndexOf("=") + 1, strLine.length()))
				}
				if(strLine.contains(Constants.CONF_DEN_NV1)) {
					rateMap["nv1"] = Integer.parseInt(strLine.substring(strLine.lastIndexOf("=") + 1, strLine.length()))
				}
				if(strLine.contains(Constants.CONF_DEN_NV2)) {
					rateMap["nv2"] = Integer.parseInt(strLine.substring(strLine.lastIndexOf("=") + 1, strLine.length()))
				}
				if(strLine.contains(Constants.CONF_DEN_CTY)) {
					rateMap["cty"] = Integer.parseInt(strLine.substring(strLine.lastIndexOf("=") + 1, strLine.length()))
				}
				result = ["success" : true, "rate" : rateMap]
			}
		} catch (Exception ex){
			log.error("Unexpected error happened during reading file", ex)
			result = ["success" : false, "msg" : message(code : "default.thientan.messages.readFileFailed", args : [null])]
		} finally {
			br.close()
			fis.close()
			render result as JSON
		}
	}

	protected Map buildExportData(Map exportParams) {
		Long hopDongId = Long.valueOf(params["id"])
		List<ChiTietThuChi> cttcs = ChiTietThuChi.withCriteria {
			fetchMode("nhanVien", FetchMode.JOIN)
			eq("hopDong.id", hopDongId)
		}
		return ["data" : extractPropertiesAsMaps(cttcs, [
				"soTien",
				"nhanVien.hoTen",
				"loaiChiTiet"
			]),
			"maHd" : params["maHd"]]
	}

	@Override
	protected String getExportFileName(){
		"ChiTietPhieuThu"
	}

	def importData = {
		Map result = [:]
		MultipartFile file = request.getFile('fileUpload')
		if(!file.empty) { // File is found in request
			if(file.size > Constants.IMPORT_FILE_SIZE_MAX) {
				result = getFormMessage(false, "error", message(code : "default.thientan.import.fileTooBig", args : null), null)
			} else if (!file.originalFilename.endsWith(Constants.XLS_FORMAT) && 
					   !file.originalFilename.endsWith(Constants.XLSX_FORMAT)) { // Just accept Excel file
				result = getFormMessage(true, "error", message(code : "default.thientan.import.unsupportedFileFormat", args : null), null)
			} else {
				// Build filename to make file and store in a server's directory
				String timeInstance = new SimpleDateFormat(Constants.DEFAULT_TIME_FORMAT).format(Calendar.getInstance().getTime())
				String extension = file.originalFilename.endsWith(Constants.XLS_FORMAT) ? Constants.XLS_FORMAT : Constants.XLSX_FORMAT
				String fileName = Constants.IMPORT_FILE_NAME + timeInstance + extension
				String fullPath = servletContext.getRealPath("") + File.separator + "import" + 
																   File.separator + fileName

				// Create new file at server with content was transferred from uploaded file
				File importFile = new File(fullPath)
				if (importFile.getParent() != null && !importFile.getParentFile().exists()) { // Create folder if it doesn't exist
					importFile.getParentFile().mkdirs()
				}
				file.transferTo(importFile)
				
				try {
					Map importResult = phieuThuService.processImport(importFile, extension)
					result = buildMessages(result, importResult)
				} catch (Exception e) {
					log.error("Error occurred when processing the import file: ", e)
					result = getFormMessage(false, "error",
						message(code : "default.thientan.import.failed", args : null), null)
				}
			}
		}
		def json = new JSON(result)
        render json.toString()
	}

	private Map buildMessages(Map result, Map importResult) {
		result = getFormMessage(true, "info", message(code : "default.thientan.import.success", args : null), null)
		String updated = message(code : "default.thientan.import.updateLines", args : [importResult["updated"]])
		result.putAt("updated", updated)
		String emptyLines = importResult["emptyLines"] ? message(code : "default.thientan.import.emptyLines", args : [importResult["emptyLines"].join(", ")]) : Constants.EMPTY_STR
		String emptyMaHds = importResult["emptyMaHds"] ? message(code : "default.thientan.import.emptyMaHds", args : [importResult["emptyMaHds"].join(", ")]) : Constants.EMPTY_STR
		String emptySoTiens = importResult["emptySoTiens"] ? message(code : "default.thientan.import.emptySoTiens", args : [importResult["emptySoTiens"].join(", ")]) : Constants.EMPTY_STR
		String invalidSoTiens = importResult["invalidSoTiens"] ? message(code : "default.thientan.import.emptySoTiens", args : [importResult["invalidSoTiens"].join(", ")]) : Constants.EMPTY_STR
		String exceedAmountHds = importResult["exceedAmountHds"] ? message(code : "default.thientan.import.exceedAmountHds", args : [importResult["exceedAmountHds"].join(", ")]) : Constants.EMPTY_STR
		result.putAt("error", emptyLines + emptyMaHds + emptySoTiens + invalidSoTiens + exceedAmountHds)
		result
	}
}
